﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Threading.Tasks;
using System.Windows.Input;

namespace VisualTools.Controls
{
    public class WpfWindow : Window
    {
        public static readonly new DependencyProperty WindowStateProperty =
            DependencyProperty.Register("WindowState", typeof(WindowState), typeof(WpfWindow), new PropertyMetadata(WindowState.Normal, OnWindowStateChanged));

        public static readonly new DependencyProperty TitleProperty =
            DependencyProperty.Register("Title", typeof(object), typeof(WpfWindow), new FrameworkPropertyMetadata(null, FrameworkPropertyMetadataOptions.AffectsMeasure, OnTitleChanged));

        public static readonly DependencyProperty TitleTextProperty =
            DependencyProperty.Register("TitleText", typeof(string), typeof(WpfWindow), new PropertyMetadata(string.Empty));

        public static readonly DependencyProperty BorderOpacityProperty =
            DependencyProperty.Register("BorderOpacity", typeof(double), typeof(WpfWindow), new PropertyMetadata(1.0));

        public static readonly new DependencyProperty ResizeModeProperty =
            DependencyProperty.Register("ResizeMode", typeof(ResizeMode), typeof(WpfWindow), new PropertyMetadata(ResizeMode.CanResize, OnResizeModeChanged));

        public static readonly DependencyProperty CanCloseProperty =
            DependencyProperty.Register("CanClose", typeof(bool), typeof(WpfWindow), new PropertyMetadata(true));

        public static readonly DependencyProperty ThemeProperty =
            DependencyProperty.Register("Theme", typeof(Theme), typeof(WpfWindow), new PropertyMetadata(OnThemeChanged));

        public static readonly DependencyProperty IsMovableProperty =
            DependencyProperty.Register("IsMovable", typeof(bool), typeof(WpfWindow), new PropertyMetadata(true));

        internal static readonly DependencyProperty CanMaximizeProperty =
            DependencyProperty.Register("CanMaximize", typeof(bool), typeof(WpfWindow), new PropertyMetadata(true));

        internal static readonly DependencyProperty CanMinimizeProperty =
            DependencyProperty.Register("CanMinimize", typeof(bool), typeof(WpfWindow), new PropertyMetadata(true));

        internal static readonly DependencyProperty CanNormalizeProperty =
            DependencyProperty.Register("CanNormalize", typeof(bool), typeof(WpfWindow), new PropertyMetadata(true));

        public static void OnThemeChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            var window = d as WpfWindow;
            window.ChangeTheme((Theme)e.NewValue);
        }

        public static void OnTitleChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            var window = d as WpfWindow;
            window.UpdateTitleBase();
        }

        public static void OnWindowStateChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            var window = d as WpfWindow;
            var state = (WindowState)e.NewValue;
            switch (state)
            {
                case WindowState.Maximized: window.Maximize(); break;
                case WindowState.Normal: window.Normalize(); break;
                case WindowState.Minimized: window.Minimize(); break;
            }

            window.SetVisualElements();
        }

        public static void OnResizeModeChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            var window = d as WpfWindow;
            var mode = (ResizeMode)e.NewValue;
            window.ChangeResizeMode(mode);

            window.SetVisualElements();
        }

        private Border TitleBar;
        private Border ContentBorderOutside;
        private double _horizontalChange, _verticalChange;
        private bool _isMaximizeAllowed = true;

        public new WindowState WindowState
        {
            get { return (WindowState)this.GetValue(WindowStateProperty); }
            set { this.SetValue(WindowStateProperty, value); }
        }

        public new object Title
        {
            get { return (object)this.GetValue(TitleProperty); }
            set { this.SetValue(TitleProperty, value); }
        }

        public string TitleText
        {
            get { return (string)this.GetValue(TitleTextProperty); }
            set { this.SetValue(TitleTextProperty, value); }
        }

        public new ResizeMode ResizeMode
        {
            get { return (ResizeMode)this.GetValue(ResizeModeProperty); }
            set { this.SetValue(ResizeModeProperty, value); }
        }

        public bool CanClose
        {
            get { return (bool)this.GetValue(CanCloseProperty); }
            set { this.SetValue(CanCloseProperty, value); }
        }

        public bool IsMovable
        {
            get { return (bool)this.GetValue(IsMovableProperty); }
            set { this.SetValue(IsMovableProperty, value); }
        }

        public Theme Theme
        {
            get { return (Theme)this.GetValue(ThemeProperty); }
            set { this.SetValue(ThemeProperty, value); }
        }

        public bool CanMaximize
        {
            get { return (bool)this.GetValue(CanMaximizeProperty); }
            set { this.SetValue(CanMaximizeProperty, value); }
        }

        public bool CanMinimize
        {
            get { return (bool)this.GetValue(CanMinimizeProperty); }
            set { this.SetValue(CanMinimizeProperty, value); }
        }

        public bool CanNormalize
        {
            get { return (bool)this.GetValue(CanNormalizeProperty); }
            set { this.SetValue(CanNormalizeProperty, value); }
        }

        public double BorderOpacity
        {
            get { return (double)this.GetValue(BorderOpacityProperty); }
            set { this.SetValue(BorderOpacityProperty, value); }
        }

        public WpfWindow()
        {
            // Add this window to the manager
            WindowManager.AddWindow(this);

            ResourceDictionary dict = new ResourceDictionary();
            dict.Source = new Uri("pack://application:,,,/VisualTools.Controls;component/Themes.xaml");
            this.Resources.MergedDictionaries.Add(dict);

            if (this.Theme == Controls.Theme.Blue)
                ChangeTheme(Controls.Theme.Blue);

            this.WindowStyle = System.Windows.WindowStyle.None;
            this.AllowsTransparency = true;

            this.SourceInitialized += new EventHandler(WpfWindow_SourceInitialized);
            //this.StateChanged += new EventHandler(WpfWindow_StateChanged);
            this.Closing += new System.ComponentModel.CancelEventHandler(WpfWindow_Closing);
        }

        private void ChangeResizeMode(ResizeMode mode)
        {
            base.ResizeMode = mode;
        }

        private void ChangeTheme(Theme theme)
        {
            this.Style = Resources[theme.ToString() + "Theme"] as Style;
            ApplyTemplate();
            SetElementEvents();
        }

        private void WpfWindow_Closing(object sender, System.ComponentModel.CancelEventArgs e)
        {
            if (!this.CanClose)
            {
                e.Cancel = true;
            }
            else
                WindowManager.RemoveWindow(this);
        }

        private void Maximize()
        {
            base.WindowState = System.Windows.WindowState.Maximized;

            // we need to shrink the borders on the window, but we need to wait until the Borders have been created
            Task a = Task.Factory.StartNew(() =>
                {
                    while (ContentBorderOutside == null) { System.Threading.Thread.Sleep(100); }
                    // Shrink the borders
                    this.Dispatcher.Invoke((Action)delegate { ContentBorderOutside.Margin = new Thickness(-7, 0, -7, -7); });
                });
        }

        private void Normalize()
        {
            base.WindowState = System.Windows.WindowState.Normal;
            // re-create borders
            if (ContentBorderOutside != null)
                ContentBorderOutside.Margin = new Thickness(0);
        }
        
        private void Minimize()
        {
            base.WindowState = System.Windows.WindowState.Minimized;
        }

        private void UpdateTitleBase()
        {
            base.Title = GetWindowTitleText();
        }

        private string GetWindowTitleText()
        {
            if (this.Title is string)
                return this.Title.ToString();
            else
            {
                return this.TitleText;
            }
        }

        private void SetVisualElements()
        {
            this.CanMaximize = CheckCanMaximize();
            this.CanMinimize = CheckCanMinimize();
            this.CanNormalize = CheckCanNormalize();
        }

        private void SetElementEvents()
        {
            // get the elements in the style 
            var MaximizeButton = (Button)this.Template.FindName("MaximizeButton", this);
            var NormalizeButton = (Button)this.Template.FindName("NormalizeButton", this);
            var MinimizeButton = (Button)this.Template.FindName("MinimizeButton", this);
            var CloseButton = (Button)this.Template.FindName("CloseButton", this);
            TitleBar = (Border)this.Template.FindName("TitleBar", this);
            ContentBorderOutside = (Border)this.Template.FindName("WhiteContentBorder", this);
            var RightSideThumb = (System.Windows.Controls.Primitives.Thumb)this.Template.FindName("RightSideThumb", this);
            var BottomSideThumb = (System.Windows.Controls.Primitives.Thumb)this.Template.FindName("BottomSideThumb", this);
            var CornerThumb = (System.Windows.Controls.Primitives.Thumb)this.Template.FindName("CornerThumb", this);
            var RestoreMenuItem = (MenuItem)this.Template.FindName("RestoreMenuItem", this);
            var MaximizeMenuItem = (MenuItem)this.Template.FindName("MaximizeMenuItem", this);
            var MinimizeMenuItem = (MenuItem)this.Template.FindName("MinimizeMenuItem", this);
            var CloseMenuItem = (MenuItem)this.Template.FindName("CloseMenuItem", this);
            var WindowIcon = (Image)this.Template.FindName("WindowIcon", this);
            var WindowContextMenu = (ContextMenu)this.Template.FindName("WindowContextMenu", this);

            // events
            if (MaximizeButton != null)
            {
                #region MaximizeButton Events
                MaximizeButton.Click += (s, e) =>
                {
                    this.WindowState = System.Windows.WindowState.Maximized;
                };
                #endregion  MaximizeButton Events
            }
            if (NormalizeButton != null)
            {
                #region NormalizeButton Events
                NormalizeButton.Click += (s, e) =>
                {
                    this.WindowState = System.Windows.WindowState.Normal;
                };
                #endregion  NormalizeButton Events
            }
            if (MinimizeButton != null)
            {
                #region MinimizeButton Events
                MinimizeButton.Click += (s, e) =>
                {
                    this.WindowState = System.Windows.WindowState.Normal;
                    this.WindowState = System.Windows.WindowState.Minimized;
                };
                #endregion MinimizeButton Events
            }
            if (CloseButton != null)
            {
                #region CloseButton Events
                CloseButton.Click += (s, e) =>
                {
                    this.Close();
                };
                #endregion CloseButton Events
            }
            if (TitleBar != null)
            {
                #region TitleBar Events
                    TitleBar.PreviewMouseLeftButtonDown += (s, e) =>
                    {
                        if (this.IsMovable && TitleBar.IsMouseDirectlyOver)
                        {
                            if (e.LeftButton == System.Windows.Input.MouseButtonState.Pressed)
                            {
                                TitleBar.Cursor = Cursors.SizeAll;
                                this.DragMove();
                                TitleBar.Cursor = Cursors.Arrow;
                            }
                        }
                    };                
                #endregion TitleBar Events
            }
            if (RightSideThumb != null)
            {
                #region RightSideThumb Events
                RightSideThumb.DragStarted += (s, e) =>
                {
                    RightSideThumb.Cursor = System.Windows.Input.Cursors.SizeWE;
                };
                RightSideThumb.DragDelta += (s, e) =>
                {
                    var newWidth = this.Width + e.HorizontalChange;
                    if (newWidth >= 0 && newWidth >= this.MinWidth && newWidth <= this.MaxWidth)
                    {

                        this.Width += e.HorizontalChange - _horizontalChange;
                        _horizontalChange = e.HorizontalChange;
                    }
                    e.Handled = true;
                };
                RightSideThumb.DragCompleted += (s, e) =>
                {
                    RightSideThumb.Cursor = System.Windows.Input.Cursors.Arrow;
                    _horizontalChange = 0;
                };
                #endregion RightSideThumb Events
            }
            if (BottomSideThumb != null)
            {
                #region BottomSideThumb Events
                BottomSideThumb.DragStarted += (s, e) =>
                {
                    BottomSideThumb.Cursor = System.Windows.Input.Cursors.SizeNS;
                };
                BottomSideThumb.DragDelta += (s, e) =>
                {
                    var newHeight = this.Height + e.VerticalChange;
                    if (newHeight >= 40 && newHeight >= this.MinHeight && newHeight <= this.MaxHeight)
                    {
                        this.Height += e.VerticalChange - _verticalChange;
                        _verticalChange = e.VerticalChange;
                    }
                    e.Handled = true;
                };
                BottomSideThumb.DragCompleted += (s, e) =>
                {
                    BottomSideThumb.Cursor = System.Windows.Input.Cursors.Arrow;
                    _verticalChange = 0;
                };
                #endregion BottomSideThumb Events
            }
            if (CornerThumb != null)
            {
                #region CornerThumb Events
                CornerThumb.DragStarted += (s, e) =>
                {
                    CornerThumb.Cursor = System.Windows.Input.Cursors.SizeNWSE;
                };
                CornerThumb.DragDelta += (s, e) =>
                {
                    var newHeight = this.Height + e.VerticalChange;
                    if (newHeight >= 40 && newHeight >= this.MinHeight && newHeight <= this.MaxHeight)
                    {
                        this.Height += e.VerticalChange - _verticalChange;
                        _verticalChange = e.VerticalChange;
                    }
                    var newWidth = this.Width + e.HorizontalChange;
                    if (newWidth >= 0 && newWidth >= this.MinWidth && newWidth <= this.MaxWidth)
                    {

                        this.Width += e.HorizontalChange - _horizontalChange;
                        _horizontalChange = e.HorizontalChange;
                    }
                    e.Handled = true;
                };
                CornerThumb.DragCompleted += (s, e) =>
                {
                    CornerThumb.Cursor = System.Windows.Input.Cursors.Arrow;
                    _verticalChange = 0;
                    _horizontalChange = 0;
                };
                #endregion CornerThumb Events
            }
            if (RestoreMenuItem != null)
            {
                #region RestoreMenuItem Events
                RestoreMenuItem.Click += (s, e) =>
                {
                    this.WindowState = System.Windows.WindowState.Normal;
                };
                #endregion  RestoreMenuItem Events
            }
            if (MaximizeMenuItem != null)
            {
                #region MaximizeMenuItem Events
                MaximizeMenuItem.Click += (s, e) =>
                {
                    this.WindowState = System.Windows.WindowState.Maximized;
                };
                #endregion  MaximizeMenuItem Events
            }
            if (MinimizeMenuItem != null)
            {
                #region MinimizeMenuItem Events
                MinimizeMenuItem.Click += (s, e) =>
                {
                    this.WindowState = System.Windows.WindowState.Minimized;
                };
                #endregion MinimizeMenuItem Events
            }
            if (CloseMenuItem != null)
            {
                #region CloseMenuItem Events
                CloseMenuItem.Click += (s, e) =>
                {
                    this.Close();
                };
                #endregion  CloseMenuItem Events
            }
            if (WindowIcon != null && WindowContextMenu != null)
            {
                #region WindowIcon Events
                WindowIcon.PreviewMouseLeftButtonDown += (s, e) =>
                {
                    WindowContextMenu.IsOpen = true;
                };
                #endregion  WindowIcon Events
            }

            SetVisualElements();
        }

        private bool CheckCanMaximize()
        {
            if (this.ResizeMode == System.Windows.ResizeMode.CanMinimize || this.ResizeMode == System.Windows.ResizeMode.NoResize || !_isMaximizeAllowed)
                return false;
            if (this.WindowState == System.Windows.WindowState.Maximized)
                return false;
            return true;
        }

        private bool CheckCanMinimize()
        {
            if (this.ResizeMode == System.Windows.ResizeMode.NoResize)
                return false;
            return true;
        }

        private bool CheckCanNormalize()
        {
            if (this.ResizeMode == System.Windows.ResizeMode.CanMinimize || this.ResizeMode == System.Windows.ResizeMode.NoResize || !_isMaximizeAllowed)
                return false;
            if (this.WindowState == System.Windows.WindowState.Normal)
                return false;
            if (this.WindowState == System.Windows.WindowState.Minimized)
                return false;
            return true;
        }

        private void WpfWindow_SourceInitialized(object sender, EventArgs e)
        {
            if (double.IsInfinity(this.MaxHeight))
            {
                this.MaxHeight = System.Windows.SystemParameters.MaximizedPrimaryScreenHeight;
            }
            else
                _isMaximizeAllowed = false;
            if (double.IsInfinity(this.MaxWidth))
            {
                this.MaxWidth = System.Windows.SystemParameters.MaximizedPrimaryScreenWidth;
            }
            else
                _isMaximizeAllowed = false;
            SetElementEvents();
        }
    }
}
